/*
 * AnimationSample.cs
 * Authors: Bruno Evangelista
 *          Rodrigo 'r2d2rigo' Diaz
 * Copyright (c) 2008 Bruno Evangelista. All rights reserved.
 * Copyright (c) 2010 Rodrigo 'r2d2rigo' Diaz. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAnimation;
using XNAnimation.Controllers;
using XNAnimation.Effects;

namespace AttachModel
{
    /// <summary>
    /// This sample ilustrates how to use an animation controller
    /// </summary>
    public class XNAnimationSample : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;

        /// <summary>
        /// Graphics 2D stuff
        /// </summary>
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;

        /// <summary>
        /// View and Projection camera matrices 
        /// </summary>
        Matrix cameraView;
        Matrix cameraProjection;

        /// <summary>
        /// Stores the last keyboard state
        /// </summary>
        KeyboardState lastKeyboradState;

        /// <summary>
        /// The SkinnedModel class handles skeletal animated models
        /// </summary>
        SkinnedModel skinnedModel;

        /// <summary>
        /// The weapon model to be attached to the character
        /// </summary>
        SkinnedModel weaponModel;

        /// <summary>
        /// The AnimationController class handles the animation playback
        /// </summary>
        AnimationController animationController;
        
        int activeAnimationClipIndex = 0;
        bool enableInterpolation = false;
        
        public XNAnimationSample()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferMultiSampling = true;

            Content.RootDirectory = "Content";
        }

        protected override void LoadContent()
        {
            // Create a SpriteBatch and a SpritFont for 2D drawing
            spriteBatch = new SpriteBatch(GraphicsDevice);
            spriteFont = Content.Load<SpriteFont>("Fonts/Arial");

            // Setup camera
            cameraView = Matrix.CreateLookAt(new Vector3(10, 15, 30), new Vector3(0, 14, 0), Vector3.Up);
            cameraProjection = Matrix.CreatePerspectiveFieldOfView(1, 1, 1, 1000);

            // Loads an animated model
            skinnedModel = Content.Load<SkinnedModel>("Models/PlayerMarine");

            weaponModel = Content.Load<SkinnedModel>("Models/WeaponMachineGun");

            // Creates an animation controller
            animationController = new AnimationController(skinnedModel.SkeletonBones);

            // Start the first animation stored in the AnimationClips dictionary
            animationController.StartClip(
                skinnedModel.AnimationClips.Values[activeAnimationClipIndex]);
        }


        protected override void Update(GameTime gameTime)
        {
            #region Keyboard Handling
            KeyboardState keyboradState = Keyboard.GetState();

            // Exit
            if (keyboradState.IsKeyDown(Keys.Escape))
                Exit();

            // 'Up' and 'Down' changes the animation speed
            if (keyboradState.IsKeyDown(Keys.Up))
            {
                animationController.Speed += 0.005f;
            }
            else if (keyboradState.IsKeyDown(Keys.Down))
            {
                animationController.Speed = (animationController.Speed < 0.1f) ?
                    0.1f : animationController.Speed - 0.005f;
            }

            // 'Left' and 'Right loop through the animations
            if (keyboradState.IsKeyDown(Keys.Left) && lastKeyboradState.IsKeyUp(Keys.Left))
            {
                activeAnimationClipIndex = ((activeAnimationClipIndex - 1) < 0)?
                    skinnedModel.AnimationClips.Count - 1 : activeAnimationClipIndex - 1;
                animationController.StartClip(skinnedModel.AnimationClips.Values[activeAnimationClipIndex]);
            }
            else if (keyboradState.IsKeyDown(Keys.Right) && lastKeyboradState.IsKeyUp(Keys.Right))
            {
                activeAnimationClipIndex = (activeAnimationClipIndex + 1) % skinnedModel.AnimationClips.Count;
                animationController.StartClip(skinnedModel.AnimationClips.Values[activeAnimationClipIndex]);
            }

            // 'Space' changes forward/backward playback
            if (keyboradState.IsKeyDown(Keys.Space) && lastKeyboradState.IsKeyUp(Keys.Space))
            {
                if (animationController.PlaybackMode == PlaybackMode.Forward)
                    animationController.PlaybackMode = PlaybackMode.Backward;
                else
                    animationController.PlaybackMode = PlaybackMode.Forward;
            }

            // 'Enter' enable/disable animation interpolation
            if (keyboradState.IsKeyDown(Keys.Enter) && lastKeyboradState.IsKeyUp(Keys.Enter))
            {
                enableInterpolation = !enableInterpolation;

                if (enableInterpolation)
                {
                    animationController.TranslationInterpolation = InterpolationMode.Linear;
                    animationController.OrientationInterpolation = InterpolationMode.Spherical;
                    animationController.ScaleInterpolation = InterpolationMode.Linear;
                }
                else
                {
                    animationController.TranslationInterpolation = InterpolationMode.None;
                    animationController.OrientationInterpolation = InterpolationMode.None;
                    animationController.ScaleInterpolation = InterpolationMode.None;
                }
            }

            lastKeyboradState = keyboradState;

            #endregion

            // Update the animation according to the elapsed time
            animationController.Update(gameTime.ElapsedGameTime, Matrix.Identity);

            base.Update(gameTime);
        }


        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.Gray);
            graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
            graphics.GraphicsDevice.BlendState = BlendState.Opaque;

            skinnedModel.World = Matrix.Identity;
            skinnedModel.View = cameraView;
            skinnedModel.Projection = cameraProjection;
            skinnedModel.BoneTransforms = animationController.SkinnedBoneTransforms;
            skinnedModel.DiffuseColor = new Vector3(0.8f);
            skinnedModel.SpecularColor = new Vector3(0.3f);
            skinnedModel.SpecularPower = 8;

            // OPTIONAL - Configure lights
            skinnedModel.AmbientLightColor = new Vector3(0.1f);
            skinnedModel.LightEnabled = true;
            skinnedModel.EnabledLights = EnabledLights.One;
            skinnedModel.PointLights[0].Color = Vector3.One;
            skinnedModel.PointLights[0].Position = new Vector3(100);

            skinnedModel.Draw();

            weaponModel.World = animationController.GetBoneAbsoluteTransform("R_Hand2");
            weaponModel.View = cameraView;
            weaponModel.Projection = cameraProjection;
            weaponModel.DiffuseColor = new Vector3(0.8f);
            weaponModel.SpecularColor = new Vector3(0.3f);
            weaponModel.SpecularPower = 8;

            // OPTIONAL - Configure lights
            weaponModel.AmbientLightColor = new Vector3(0.1f);
            weaponModel.LightEnabled = true;
            weaponModel.EnabledLights = EnabledLights.One;
            weaponModel.PointLights[0].Color = Vector3.One;
            weaponModel.PointLights[0].Position = new Vector3(100);

            weaponModel.Draw();

            Draw2D();

            base.Draw(gameTime);
        }

        #region Draw 2D Stuff
        private void Draw2D()
        {
            spriteBatch.Begin();

            spriteBatch.DrawString(spriteFont, "Use [LEFT/RIGHT] to start a new animation clip.",
                new Vector2(10, 5), Color.White);
            spriteBatch.DrawString(spriteFont, "Use [UP/DOWN] to change the animation speed.",
                new Vector2(10, 22), Color.White);
            spriteBatch.DrawString(spriteFont, "Use [SPACE] to change the animation playback mode.",
                new Vector2(10, 39), Color.White);
            spriteBatch.DrawString(spriteFont, "Use [ENTER] to enable/disable animation interpolation.",
                new Vector2(10, 56), Color.White);

            spriteBatch.DrawString(spriteFont, "Model courtesy of FloatBox Studios",
                new Vector2(560, 580), Color.White);

            spriteBatch.End();

        }
        #endregion

        #region Entry Point
        static void Main(string[] args)
        {
            using (XNAnimationSample game = new XNAnimationSample())
            {
                game.Run();
            }
        }
        #endregion
    }
}